﻿#light

open System
open System.Collections.Generic
open System.Runtime.InteropServices
open Dwarf.Managed
open MPI

[<DllImport(@"CliTools.dll")>]
extern void start()

[<DllImport(@"CliTools.dll")>]
extern double finish()

[<DllImport(@"CliTools.dll")>]
extern void usage()

let listUnion(listFirst : List<int>)(listSecond : List<int>) : List<int> =
    listFirst.AddRange(listSecond)
    listFirst

let solve(dwarfConfigurator : Configurator, patternVal : string, statesVal : int[,], commsize : int, iterCount : int, rank : int, root : int) : List<int> = 
    let mutable positions = new List<int>()
    let mutable initialPositionVal = 0
    for i = 0 to iterCount-1 do
        let mutable contentVal = null : string
        if rank = root then
            for proc = 1 to commsize-1 do
                contentVal <- dwarfConfigurator.GetNextInputPart()
                MPI.Intracommunicator.world.Send(initialPositionVal, proc, root)
                MPI.Intracommunicator.world.Send(contentVal, proc, root)
                initialPositionVal <- initialPositionVal + contentVal.Length - (int)contentVal.[contentVal.Length - 1] - 1
            contentVal <- dwarfConfigurator.GetNextInputPart()
        else
            let mutable content = null : string
            MPI.Intracommunicator.world.Receive(root, MPI.Intracommunicator.anyTag, &initialPositionVal)
            MPI.Intracommunicator.world.Receive(root, MPI.Intracommunicator.anyTag, &content)
            contentVal <- content
        let contentLengthVal = contentVal.Length - (int)contentVal.[contentVal.Length - 1] - 1
        let fsm : FiniteStateMachineFSharpMethod.fsmType =
            {
                pattern = patternVal;
                content = contentVal;
                states = statesVal;
                stateDimensionCoincidence = patternVal.Length;
                initialPosition = initialPositionVal;
                positions = new List<int>();
                contentLength = contentLengthVal
            }
        FiniteStateMachineFSharpMethod.solve(fsm)
        positions.AddRange(fsm.positions)
        if rank = root then
            initialPositionVal <- initialPositionVal + contentLengthVal

    let reduceOperation = new ReductionOperation<List<int>>(listUnion)
    MPI.Intracommunicator.world.Reduce<List<int>>(positions, reduceOperation, root)
    positions.Sort()
    positions

let main() =
    let args = System.Environment.GetCommandLineArgs()
    let mutable dwarfConfigurator = null
    let mutable statesVal = null : int[,]       // matrix of states
    let mutable patternVal = null  : string     //  
    let mutable initialPositionVal = 0          // values of fsmType members for each string part
    let mutable iterCount = 0
    let mutable stateDimensionCoincidence = 0

    let mpienv = new MPI.Environment(ref args)
    let rank = MPI.Communicator.world.Rank
    let incomm = MPI.Intracommunicator.world
    let commsize = incomm.Size
    let root = 0 : int   

    start()

    if rank = root then
        dwarfConfigurator <- new Configurator(args)        
        dwarfConfigurator.WriteSettings()
        let contentPartCount = dwarfConfigurator.GetData(&patternVal, commsize)
        iterCount <- contentPartCount / commsize

        stateDimensionCoincidence <- patternVal.Length
        statesVal <- FiniteStateMachineFSharpMethod.constructStates(patternVal)                          
    else
        statesVal <- Array2D.zeroCreate<int> 65535 stateDimensionCoincidence

    incomm.Broadcast<string>(&patternVal, root)
    incomm.Broadcast<int>(&iterCount, root)
    incomm.Broadcast<int>(&stateDimensionCoincidence, root)
    incomm.Broadcast<int[,]>(&statesVal, root)

    let positions = solve(dwarfConfigurator, patternVal, statesVal, commsize, iterCount, rank, root)

    let time = finish()

    if rank = root then
        dwarfConfigurator.Close(time, positions)
        Console.WriteLine("Total count of pattern entries: " + (string)positions.Count)
    mpienv.Dispose()

main()
